/*******************************************************************************
 * Copyright (c) 2009 Kevin Bierhoff and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Kevin Bierhoff - initial API and implementation
 *******************************************************************************/
package com.googlecode.twelfdevtools.wizards;

import java.lang.reflect.InvocationTargetException;
import java.net.URI;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceStatus;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.INewWizard;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchWizard;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.WizardNewProjectCreationPage;
import org.eclipse.ui.ide.undo.CreateFileOperation;
import org.eclipse.ui.ide.undo.CreateProjectOperation;
import org.eclipse.ui.ide.undo.WorkspaceUndoUtil;
import org.eclipse.ui.part.ISetSelectionTarget;
import org.eclipse.ui.statushandlers.StatusAdapter;
import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.ui.wizards.newresource.BasicNewResourceWizard;

import com.googlecode.twelfdevtools.TwelfActivator;
import com.googlecode.twelfdevtools.builder.TwelfBuilder;
import com.googlecode.twelfdevtools.builder.TwelfNature;
import com.googlecode.twelfdevtools.util.UIUtil;


/**
 * Creates a new project with the Twelf nature.
 * 
 * The implementation of this class is modeled after
 * {@link org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard}.
 * @author Kevin Bierhoff
 */
public class NewTwelfProjectWizard extends Wizard implements INewWizard {
	private WizardNewProjectCreationPage page;
	private IProject newProject;
//	private ISelection selection;
	private IWorkbench workbench;
//	private IFile newConfigFile;

	/**
	 * Constructor for NewElfWizard.
	 */
	public NewTwelfProjectWizard() {
		super();
		setNeedsProgressMonitor(true);
	}
	
	/**
	 * Adding the page to the wizard.
	 */

	public void addPages() {
		page = new WizardNewProjectCreationPage("twelfNewProjectPage") {
			public void createControl(Composite parent) {
				super.createControl(parent);
				// TODO extra input fields for Twelf projects, e.g. source file name?
			}			
		};
		page.setTitle("New Twelf Project");
		page.setDescription("Create a new Twelf project.");
		addPage(page);
		
		// TODO second page with referenced projects, like in BasicNewProjectResourceWizard?
	}

	/**
	 * Creates a new project resource with the selected name.
	 * <p>
	 * In normal usage, this method is invoked after the user has pressed Finish
	 * on the wizard; the enablement of the Finish button implies that all
	 * controls on the pages currently contain valid values.
	 * </p>
	 * <p>
	 * Note that this wizard caches the new project once it has been
	 * successfully created; subsequent invocations of this method will answer
	 * the same project resource without attempting to create it again.
	 * </p>
	 * 
	 * @return the created project resource, or <code>null</code> if the
	 *         project was not created
	 */
	private IProject createNewTwelfProject() {
		if (newProject != null) {
			return newProject;
		}

		// get a project handle
		final IProject newProjectHandle = page.getProjectHandle();

		// get a project descriptor
		URI location = null;
		if (!page.useDefaults()) {
			location = page.getLocationURI();
		}

		IWorkspace workspace = ResourcesPlugin.getWorkspace();
		final IProjectDescription description = workspace
				.newProjectDescription(newProjectHandle.getName());
		description.setLocationURI(location);

		// this is why we're doing this whole wizard:
		// add twelf nature
		addTwelfSpecifics(description);
		
		// TODO update the referenced project if provided
//		if (referencePage != null) {
//			IProject[] refProjects = referencePage.getReferencedProjects();
//			if (refProjects.length > 0) {
//				description.setReferencedProjects(refProjects);
//			}
//		}
		
		// config file information
		final IFile newConfigFileHandle = createConfigFile(newProjectHandle);

		// create the new project operation
		IRunnableWithProgress op = new IRunnableWithProgress() {
			public void run(IProgressMonitor monitor)
					throws InvocationTargetException {
				CreateProjectOperation op = new CreateProjectOperation(
						description, "New Twelf Project");
				try {
					PlatformUI.getWorkbench().getOperationSupport()
							.getOperationHistory().execute(
									op,
									monitor,
									WorkspaceUndoUtil
											.getUIInfoAdapter(getShell()));
					// must create file creation op after creating project
					// otherwise CreateFileOperation constructor freaks
					// b/c the file's parent doesn't exist
					// TODO Avoid builder run before config file was created
					// right now builder seems to run once before, which results in error
					if(! newConfigFileHandle.exists()) {
						CreateFileOperation op2 = new CreateFileOperation(
								newConfigFileHandle, 
								null /* no link */, null /* empty file */, 
								"Create Config File");
						PlatformUI.getWorkbench().getOperationSupport()
						.getOperationHistory().execute(
								op2,
								monitor,
								WorkspaceUndoUtil
										.getUIInfoAdapter(getShell()));
					}
				} 
				catch (ExecutionException e) {
					throw new InvocationTargetException(e);
				}
			}
		};

		// run the new project creation operation
		try {
			getContainer().run(true, true, op);
		} catch (InterruptedException e) {
			return null;
		} catch (InvocationTargetException e) {
			Throwable t = e.getTargetException();
			if (t instanceof ExecutionException
					&& t.getCause() instanceof CoreException) {
				CoreException cause = (CoreException) t.getCause();
				StatusAdapter status;
				if (cause.getStatus().getCode() == IResourceStatus.CASE_VARIANT_EXISTS) {
					status = new StatusAdapter(
							UIUtil
									.newStatus(
											IStatus.WARNING,
											"The underlying file system is case insensitive. There is an existing project or directory that conflicts with " +
															newProjectHandle
																	.getName(),
											cause));
				} else {
					status = new StatusAdapter(UIUtil.newStatus(cause
							.getStatus().getSeverity(),
							"Creation Problems", cause));
				}
//				status.setProperty(IStatusAdapterConstants.TITLE_PROPERTY,
//						ResourceMessages.NewProject_errorMessage);
				StatusManager.getManager().handle(status, StatusManager.BLOCK);
			} else {
				StatusAdapter status = new StatusAdapter(new Status(
						IStatus.WARNING, TwelfActivator.PLUGIN_ID, 0,
						"Internal error: " + t.getMessage(), t));
//				status.setProperty(IStatusAdapterConstants.TITLE_PROPERTY,
//						ResourceMessages.NewProject_errorMessage);
				StatusManager.getManager().handle(status,
						StatusManager.LOG | StatusManager.BLOCK);
			}
			return null;
		}

		newProject = newProjectHandle;
//		newConfigFile = newConfigFileHandle;
		return newProject;
	}

	private IFile createConfigFile(IProject newProjectHandle) {
		return newProjectHandle.getFile("sources.cfg");
	}

	/**
	 * Creates a file resource handle for the file with the given workspace
	 * path. This method does not create the file resource; this is the
	 * responsibility of <code>createFile</code>.
	 * 
	 * @param filePath
	 *            the path of the file resource to create a handle for
	 * @return the new file resource handle
	 * @see #createFile
	 */
	protected IFile createFileHandle(IPath filePath) {
		return UIUtil.getPluginWorkspace().getRoot().getFile(
				filePath);
	}

	private void addTwelfSpecifics(final IProjectDescription description) {
		String[] natures = description.getNatureIds();
		boolean isTwelf = false;
		for (int i = 0; i < natures.length; ++i) {
			if (TwelfNature.NATURE_ID.equals(natures[i])) {
				isTwelf = true;
			}
		}

		if(!isTwelf) {
			// Add the nature to the initial description
			String[] newNatures = new String[natures.length + 1];
			System.arraycopy(natures, 0, newNatures, 0, natures.length);
			newNatures[natures.length] = TwelfNature.NATURE_ID;
			description.setNatureIds(newNatures);
		}

		// seem to need to explicitly set the builder, too
		isTwelf = false;
		ICommand[] commands = description.getBuildSpec();
		for (int i = 0; i < commands.length; ++i) {
			if (commands[i].getBuilderName().equals(TwelfBuilder.BUILDER_ID)) {
				isTwelf = true;
			}
		}

		if(!isTwelf) {
			// add Twelf builder
			ICommand[] newCommands = new ICommand[commands.length + 1];
			System.arraycopy(commands, 0, newCommands, 0, commands.length);
			ICommand command = description.newCommand();
			command.setBuilderName(TwelfBuilder.BUILDER_ID);
			newCommands[newCommands.length - 1] = command;
			description.setBuildSpec(newCommands);
		}
	}

	/**
	 * This method is called when 'Finish' button is pressed in
	 * the wizard. We will create an operation and run it
	 * using wizard as execution context.
	 */
	public boolean performFinish() {
		createNewTwelfProject();

		if (newProject == null) {
			return false;
		}

		// TODO working set, perspective?
//		IWorkingSet[] workingSets = mainPage.getSelectedWorkingSets();
//		getWorkbench().getWorkingSetManager().addToWorkingSets(newProject,
//				workingSets);
//        
//		updatePerspective();
		selectAndReveal(newProject);

		return true;
	}
	
    /**
     * Selects and reveals the newly added resource in all parts
     * of the active workbench window's active page.
     *
     * @see ISetSelectionTarget
     */
    protected void selectAndReveal(IResource newResource) {
        BasicNewResourceWizard.selectAndReveal(newResource, workbench.getActiveWorkbenchWindow());
    }

	/**
	 * Remember the workbench, forget the selection.
	 * @see IWorkbenchWizard#init(IWorkbench, IStructuredSelection)
	 */
	public void init(IWorkbench workbench, IStructuredSelection selection) {
		this.workbench = workbench;
//		this.selection = selection;
	}
}